/**
 * Copyright (C) 2025 pony working team
 * SPDX-License-Identifier: MulanPSL-2.0
 */
/**
 ******************************************************************************
 * @file    app_usbx_device.c
 * @author  MCD Application Team
 * @brief   USBX Device applicative file
 ******************************************************************************
 * @attention
 *
 * Copyright (c) 2025 STMicroelectronics.
 * All rights reserved.
 *
 * This software is licensed under terms that can be found in the LICENSE file
 * in the root directory of this software component.
 * If no LICENSE file comes with this software, it is provided AS-IS.
 *
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "app_usbx_device.h"

/* Private includes ----------------------------------------------------------*/
#include <stdlib.h>

#include <x_config.h>
#include <k_tick.h>
#include <x_it.h>

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/

#define USBX_DEVICE_MEMORY_STACK_SIZE (CONFIG_USBX_DEVICE_MEMORY_STACK_SIZE << 10)

/* Private macro -------------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/
static ULONG storage_interface_number;
static ULONG storage_configuration_number;
static UX_SLAVE_CLASS_STORAGE_PARAMETER storage_parameter;

/* Private function prototypes -----------------------------------------------*/
static UINT USBD_ChangeFunction(ULONG Device_State);

/**
 * @brief  Application USBX Device Initialization.
 * @param  none
 * @retval status
 */
UINT MX_USBX_Device_Init(VOID)
{
    UINT ret = UX_SUCCESS;
    UCHAR *device_framework_high_speed;
    UCHAR *device_framework_full_speed;
    ULONG device_framework_hs_length;
    ULONG device_framework_fs_length;
    ULONG string_framework_length;
    ULONG language_id_framework_length;
    UCHAR *string_framework;
    UCHAR *language_id_framework;

    UCHAR *pointer;

    pointer = malloc(USBX_DEVICE_MEMORY_STACK_SIZE);
    if (pointer == NULL) {
        return UX_ERROR;
    }

    /* Initialize USBX Memory */
    if (ux_system_initialize(pointer, USBX_DEVICE_MEMORY_STACK_SIZE, UX_NULL, 0) != UX_SUCCESS) {
        return UX_ERROR;
    }

    /* Get Device Framework High Speed and get the length */
    device_framework_high_speed =
        USBD_Get_Device_Framework_Speed(USBD_HIGH_SPEED, &device_framework_hs_length);

    /* Get Device Framework Full Speed and get the length */
    device_framework_full_speed =
        USBD_Get_Device_Framework_Speed(USBD_FULL_SPEED, &device_framework_fs_length);

    /* Get String Framework and get the length */
    string_framework = USBD_Get_String_Framework(&string_framework_length);

    /* Get Language Id Framework and get the length */
    language_id_framework = USBD_Get_Language_Id_Framework(&language_id_framework_length);

    /* Install the device portion of USBX */
    if (ux_device_stack_initialize(device_framework_high_speed, device_framework_hs_length,
                                   device_framework_full_speed, device_framework_fs_length,
                                   string_framework, string_framework_length, language_id_framework,
                                   language_id_framework_length,
                                   USBD_ChangeFunction) != UX_SUCCESS) {
        return UX_ERROR;
    }

    /* Initialize the storage class parameters for the device */
    storage_parameter.ux_slave_class_storage_instance_activate = USBD_STORAGE_Activate;
    storage_parameter.ux_slave_class_storage_instance_deactivate = USBD_STORAGE_Deactivate;

    /* Store the number of LUN in this device storage instance */
    storage_parameter.ux_slave_class_storage_parameter_number_lun = STORAGE_NUMBER_LUN;

    /* Initialize the storage class parameters for reading/writing to the Flash Disk */
    storage_parameter.ux_slave_class_storage_parameter_lun[0]
        .ux_slave_class_storage_media_last_lba = USBD_STORAGE_GetMediaLastLba();

    storage_parameter.ux_slave_class_storage_parameter_lun[0]
        .ux_slave_class_storage_media_block_length = USBD_STORAGE_GetMediaBlocklength();

    storage_parameter.ux_slave_class_storage_parameter_lun[0].ux_slave_class_storage_media_type = 0;

    storage_parameter.ux_slave_class_storage_parameter_lun[0]
        .ux_slave_class_storage_media_removable_flag = STORAGE_REMOVABLE_FLAG;

    storage_parameter.ux_slave_class_storage_parameter_lun[0]
        .ux_slave_class_storage_media_read_only_flag = STORAGE_READ_ONLY;

    storage_parameter.ux_slave_class_storage_parameter_lun[0].ux_slave_class_storage_media_read =
        USBD_STORAGE_Read;

    storage_parameter.ux_slave_class_storage_parameter_lun[0].ux_slave_class_storage_media_write =
        USBD_STORAGE_Write;

    storage_parameter.ux_slave_class_storage_parameter_lun[0].ux_slave_class_storage_media_flush =
        USBD_STORAGE_Flush;

    storage_parameter.ux_slave_class_storage_parameter_lun[0].ux_slave_class_storage_media_status =
        USBD_STORAGE_Status;

    storage_parameter.ux_slave_class_storage_parameter_lun[0]
        .ux_slave_class_storage_media_notification = USBD_STORAGE_Notification;

    /* Get storage configuration number */
    storage_configuration_number = USBD_Get_Configuration_Number(CLASS_TYPE_MSC, 0);

    /* Find storage interface number */
    storage_interface_number = USBD_Get_Interface_Number(CLASS_TYPE_MSC, 0);

    /* Initialize the device storage class */
    if (ux_device_stack_class_register(_ux_system_slave_class_storage_name,
                                       ux_device_class_storage_entry, storage_configuration_number,
                                       storage_interface_number,
                                       &storage_parameter) != UX_SUCCESS) {
        return UX_ERROR;
    }

    return ret;
}

/**
 * @brief  _ux_utility_interrupt_disable
 *         USB utility interrupt disable.
 * @param  none
 * @retval none
 */
ALIGN_TYPE _ux_utility_interrupt_disable(VOID)
{
    UINT interrupt_save;
    interrupt_save = (UINT)it_lock();

    return interrupt_save;
}

/**
 * @brief  _ux_utility_interrupt_restore
 *         USB utility interrupt restore.
 * @param  flags
 * @retval none
 */
VOID _ux_utility_interrupt_restore(ALIGN_TYPE flags)
{
    it_restore(flags);
}

/**
 * @brief  _ux_utility_time_get
 *         Get Time Tick for host timing.
 * @param  none
 * @retval time tick
 */
ULONG _ux_utility_time_get(VOID)
{
    ULONG time_tick = 0U;

    time_tick = (ULONG)tick_count_get64();

    return time_tick;
}

/**
 * @brief  USBD_ChangeFunction
 *         This function is called when the device state changes.
 * @param  Device_State: USB Device State
 * @retval status
 */
static UINT USBD_ChangeFunction(ULONG Device_State)
{
    UINT status = UX_SUCCESS;

    switch (Device_State) {
    case UX_DEVICE_ATTACHED:
        break;

    case UX_DEVICE_REMOVED:
        break;

    case UX_DCD_STM32_DEVICE_CONNECTED:
        break;

    case UX_DCD_STM32_DEVICE_DISCONNECTED:
        break;

    case UX_DCD_STM32_DEVICE_SUSPENDED:
        break;

    case UX_DCD_STM32_DEVICE_RESUMED:
        break;

    case UX_DCD_STM32_SOF_RECEIVED:
        break;

    default:
        break;
    }

    return status;
}

int usbx_device_init(void)
{
    return MX_USBX_Device_Init();
}
